package project3;

import java.io.File;
import java.util.*;

/** Central interface to the all of everything.
 *
 * The central API for interacting with our IR system. Allows for both querying
 * and indexing of new material.
 */
public class InformationRetreiver
{
    private InvertedIndex index;

    /** Builds an info retreiver; starting with no files indexed */
    public InformationRetreiver()
    {
        index=new InvertedIndex();
    }

    /**Adds all files under a folder, or a single file, to the index of the IR system.
     * @param path A folder or file which the user is capable of reading.
     */
    public void index(File path)
    {
        //if in a directory, recursively index all children of directory
        if(path.isDirectory())
            for(File file : path.listFiles())
                index(file);
        else
        {
            //if file exists, and the file isn't empty, index it
            if(path.exists()&&path.length()>0)
            {
                Document doc=new Document(path);
                index.addDocument(doc);
            }
        }
    }

    /** Returns all the documents indexed by the IR system. */
    public Collection<Document> getDocuments()
    {
        return index.getDocuments();
    }

    /**Performs a query on the IR system.
     * Query is tokenized, then stemed; and all documents containing at least
     * one stem are found. When all docs have been found, goodness is calculated,
     * and the results returned.
     *
     * @param query Query for the IR system, generally a space delimited list of keywords
     * @param firstIsBest True if you want the first entry in the returned list to be the best match of the query, false if it should be worst match.
     * @return A list of search results
     */
    public List<SearchResult> findInformation(String query, boolean firstIsBest)
    {
        List<String> queryStems = getQueryTerms(query);
        //if an empty query, return all documents
        if(queryStems.size()==0)
        	return buildTotalCollectionResults();

        Collection<Document> documents = findDocuments(queryStems);
        Goodness goodness = new Goodness(documents, queryStems);
        List<SearchResult> results = getSortedSearchResults(goodness.getDocs());
        //results are in order of increasing relevance;
        //  if we want best first, switch it
        if(firstIsBest)
            java.util.Collections.reverse(results);
        return results;
    }

    /** Returns a search result list for the entire collection,
     * using stem count / doc length as the goodness function.
     */
    private List<SearchResult> buildTotalCollectionResults()
    {
    	List<SearchResult> results=new ArrayList<SearchResult>();
    	for(Document d : index.getDocuments())
    	{
    		double pretendScore = d.getStemCount() / (double)d.getDocumentLengthInBytes();
    		results.add(new SearchResult(d, pretendScore));
    	}
    	return results;
    }
    
    /**Turns a query into a list of stems for further query processing.
     *
     * Removes any stems from query that appear in 90% of documents. If
     * any portion of the query is left (ie the query was not 'the A is I')
     * returns the query list with stems removed. Otherwise, returns the original
     * query, without stop words removed.
     * @param query A text query. Generally keywords seperated by spaces.
     * @return List of stems that appeared in the query
     */
    private List<String> getQueryTerms(String query)
    {
        List<String> tokens=index.tokenizeAndStem(query);
        List<String> tokensWithoutStops=new ArrayList<String>();
        HashSet<String> stopWords=index.findStopWords(.9);
        for(String token : tokens)
            if(!stopWords.contains(token))
                tokensWithoutStops.add(token);
        if(tokensWithoutStops.size()>0)
            return tokensWithoutStops;
        return tokens;
    }

    /**Finds all documents that contain at least one stem from the list of stems */
    private Collection<Document> findDocuments(List<String> stems)
    {
        HashSet<Document> documents=new HashSet<Document>();
        for(String stem : stems)
            documents.addAll(index.findEntriesForStem(stem));
        return documents;
    }

    /** Goes from a Map of Document and Score to a sorted list of search results.
     *
     * @returns A list of search results sorted in order of increasing score.
     */
	private List<SearchResult> getSortedSearchResults(Map<Document, Double> docs)
	{
		ArrayList<SearchResult> ndocs=new ArrayList();
		for(Map.Entry<Document, Double> entry : docs.entrySet())
		{
			SearchResult nResult=new SearchResult(entry.getKey(), entry.getValue());
			int i=0;
			for(; i<ndocs.size()&&ndocs.get(i).score<nResult.score; i++);
			ndocs.add(i, nResult);
		}
		return ndocs;
	}

	/** Test function and console UI for the information retreiver.
     */
    public static void main(String[] args)
    {
        Scanner in=new Scanner(System.in);
        File indexDir;
        System.out.println("Welcome to HAL Console Edition!");
        System.out.println("If you are a TA or Professor, you may have reached this main method in error.");
        System.out.println("If so, please run 'project3.Interface'");

        do
        {
            System.out.println("Enter a directory or file you'd like indexed: ");
            indexDir=new File(in.nextLine());
        }while(!indexDir.exists());

        InformationRetreiver retreiver=new InformationRetreiver();
        retreiver.index(indexDir);
        String query;
        do
        {
            System.out.print("Enter a query: ");
            query = in.nextLine();
			List<SearchResult> docs = retreiver.findInformation(query, false);
            for(SearchResult result : docs)
                System.out.println(result);
        }while(!query.equalsIgnoreCase("quit"));
    }
}
